import json
import re
import subprocess
from typing import List, Dict, Union

import loguru

from src.config.redis_cache import RedisCache


class PortScanner:
    def __init__(self):
        self.masscan_bin = 'masscan'  # Path to masscan binary
        self.nmap_bin = 'nmap'  # Path to nmap binary
        self.redis_cache = RedisCache()

    @staticmethod
    def parse_targets(targets: str) -> List[str]:
        """Parse the targets string into a list of valid IPs/CIDRs."""
        from ipaddress import ip_network, ip_address, summarize_address_range

        def is_valid_ip_or_cidr(target: str) -> bool:
            try:
                ip_network(target)
                return True
            except ValueError:
                return False

        def expand_ip_range(start_ip: str, end_ip: str) -> List[str]:
            start = ip_address(start_ip)
            end = ip_address(end_ip)
            if start > end:
                raise ValueError("Invalid IP range: start IP is greater than end IP.")
            return [str(addr) for addr in summarize_address_range(start, end)]

        parsed_targets = []
        for target in targets.split(','):
            target = target.strip()
            if '-' in target and not is_valid_ip_or_cidr(target):
                ip_parts = target.split('-')
                if len(ip_parts) != 2 or not all(is_valid_ip_or_cidr(part) for part in ip_parts):
                    raise ValueError(f"Invalid IP range format: {target}")
                parsed_targets.extend(expand_ip_range(ip_parts[0], ip_parts[1]))
            elif is_valid_ip_or_cidr(target):
                parsed_targets.append(target)
            else:
                raise ValueError(f"Invalid target format: {target}")

        return parsed_targets

    @staticmethod
    def get_ports_to_scan(top_ports: Union[str, List[int]]) -> str:
        """Determine which ports to scan based on user input."""
        common_service_ports = {
            'top100': '7,9,13,21-23,25-26,37,53,79-81,88,106,110-111,113,119,135,139,143-144,179,199,389,427,443-445,465,513-515,543-544,548,554,587,631,646,873,990,993,995,1025-1029,1110,1433,1720,1723,1755,1900,2000-2001,2049,2121,2717,3000,3128,3306,3389,3986,4899,5000,5009,5051,5060,5101,5190,5357,5432,5631,5666,5800,5900,6000-6001,6646,7070,8000,8008-8009,8080-8081,8443,8888,9100,9999-10000,32768,49152-49157,' +
                      '21, 22, 23, 25, 53, 67, 68, 80, 88, 110, 143, 389, 443, 445, 465, 636, 873, 1433, 1521, 2015, 2049, 2181, 2375, 2376, 2379, 2380, 2881, 2888, 3000, 3001, 3100, 3306, 3389, 3888, 4000, 5000, 5236, 5432, 5672, 5900, 5901, 5984, 6030, 6041, 6081, 6082, 6379, 6650, 6667, 6789, 6800, 6900, 7051, 7070, 7071, 7077, 7474, 7684, 8042, 8080, 8081, 8088, 8091, 8092, 8093, 8094, 8095, 8096, 8123, 8161, 8333, 8443, 8500, 8848, 9000, 9001, 9042, 9072, 9083, 9090, 9092, 9195, 9200, 9300, 9411, 9600, 9876, 10000, 10050, 10051, 10911, 11111, 11800, 11810, 12201, 12345, 15010, 15672, , 16020, 18080, 19888, 20160, 20200, 21050, 24224, 26379, 27017, 30303, 50010, 50070, 50075, 54321, 55678, 61616',
            'top1000': '21, 22, 23, 25, 53, 67, 68, 80, 88, 110, 143, 389, 443, 445, 465, 636, 873, 1433, 1521, 2015, 2049, 2181, 2375, 2376, 2379, 2380, 2881, 2888, 3000, 3001, 3100, 3306, 3389, 3888, 4000, 5000, 5236, 5432, 5672, 5900, 5901, 5984, 6030, 6041, 6081, 6082, 6379, 6650, 6667, 6789, 6800, 6900, 7051, 7070, 7071, 7077, 7474, 7684, 8042, 8080, 8081, 8088, 8091, 8092, 8093, 8094, 8095, 8096, 8123, 8161, 8333, 8443, 8500, 8848, 9000, 9001, 9042, 9072, 9083, 9090, 9092, 9195, 9200, 9300, 9411, 9600, 9876, 10000, 10050, 10051, 10911, 11111, 11800, 11810, 12201, 12345, 15010, 15672, , 16020, 18080, 19888, 20160, 20200, 21050, 24224, 26379, 27017, 30303, 50010, 50070, 50075, 54321, 55678, 61616' +
                       '1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521,1524,1533,1556,1580,1583,1594,1600,1641,1658,1666,1687-1688,1700,1717-1721,1723,1755,1761,1782-1783,1801,1805,1812,1839-1840,1862-1864,1875,1900,1914,1935,1947,1971-1972,1974,1984,1998-2010,2013,2020-2022,2030,2033-2035,2038,2040-2043,2045-2049,2065,2068,2099-2100,2103,2105-2107,2111,2119,2121,2126,2135,2144,2160-2161,2170,2179,2190-2191,2196,2200,2222,2251,2260,2288,2301,2323,2366,2381-2383,2393-2394,2399,2401,2492,2500,2522,2525,2557,2601-2602,2604-2605,2607-2608,2638,2701-2702,2710,2717-2718,2725,2800,2809,2811,2869,2875,2909-2910,2920,2967-2968,2998,3000-3001,3003,3005-3007,3011,3013,3017,3030-3031,3052,3071,3077,3128,3168,3211,3221,3260-3261,3268-3269,3283,3300-3301,3306,3322-3325,3333,3351,3367,3369-3372,3389-3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,3689-3690,3703,3737,3766,3784,3800-3801,3809,3814,3826-3828,3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,3995,3998,4000-4006,4045,4111,4125-4126,4129,4224,4242,4279,4321,4343,4443-4446,4449,4550,4567,4662,4848,4899-4900,4998,5000-5004,5009,5030,5033,5050-5051,5054,5060-5061,5080,5087,5100-5102,5120,5190,5200,5214,5221-5222,5225-5226,5269,5280,5298,5357,5405,5414,5431-5432,5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678-5679,5718,5730,5800-5802,5810-5811,5815,5822,5825,5850,5859,5862,5877,5900-5904,5906-5907,5910-5911,5915,5922,5925,5950,5952,5959-5963,5987-5989,5998-6007,6009,6025,6059,6100-6101,6106,6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565-6567,6580,6646,6666-6669,6689,6692,6699,6779,6788-6789,6792,6839,6881,6901,6969,7000-7002,7004,7007,7019,7025,7070,7100,7103,7106,7200-7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,7777-7778,7800,7911,7920-7921,7937-7938,7999-8002,8007-8011,8021-8022,8031,8042,8045,8080-8090,8093,8099-8100,8180-8181,8192-8194,8200,8222,8254,8290-8292,8300,8333,8383,8400,8402,8443,8500,8600,8649,8651-8652,8654,8701,8800,8873,8888,8899,8994,9000-9003,9009-9011,9040,9050,9071,9080-9081,9090-9091,9099-9103,9110-9111,9200,9207,9220,9290,9415,9418,9485,9500,9502-9503,9535,9575,9593-9595,9618,9666,9876-9878,9898,9900,9917,9929,9943-9944,9968,9998-10004,10009-10010,10012,10024-10025,10082,10180,10215,10243,10566,10616-10617,10621,10626,10628-10629,10778,11110-11111,11967,12000,12174,12265,12345,13456,13722,13782-13783,14000,14238,14441-14442,15000,15002-15004,15660,15742,16000-16001,16012,16016,16018,16080,16113,16992-16993,17877,17988,18040,18101,18988,19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221-20222,20828,21571,22939,23502,24444,24800,25734-25735,26214,27000,27352-27353,27355-27356,27715,28201,30000,30718,30951,31038,31337,32768-32785,33354,33899,34571-34573,35500,38292,40193,40911,41511,42510,44176,44442-44443,44501,45100,48080,49152-49161,49163,49165,49167,49175-49176,49400,49999-50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,52848,52869,54045,54328,55055-55056,55555,55600,56737-56738,57294,57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,65129,65389',
            'all': '1-65535'
        }

        if isinstance(top_ports, str):
            if top_ports.lower() in common_service_ports:
                return common_service_ports[top_ports.lower()]
            elif re.match(r'^\d+-\d+$', top_ports):  # Check for port range (e.g., "80-100")
                return top_ports
            else:
                raise ValueError("Invalid option for top_ports")
        elif isinstance(top_ports, list):
            print(f"Parsed top_ports: {top_ports}")
            top_ports = [int(port) for port in top_ports]
            if all(0 <= port <= 65535 for port in top_ports):
                return ','.join(map(str, sorted(set(top_ports))))  # Remove duplicates and sort
            else:
                raise ValueError("Invalid port numbers in the custom list")
        else:
            raise ValueError("Invalid type for top_ports")

    def run_masscan(self, targets: List[str], ports: str = '1-1000', rate: int = 1000, uuid=None) -> List[
        Dict[str, any]]:
        """Run a masscan scan on the specified targets for host discovery and return live IPs with open ports."""
        live_hosts = []
        dead_hosts = []

        for target in targets:
            command = [self.masscan_bin, '-p', ports, '--rate', str(rate), target]
            print(f"Executing: {' '.join(command)}")

            # 使用 subprocess.run 代替 subprocess.Popen
            try:
                result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
                                        timeout=60 * 60 * 12)
            except subprocess.TimeoutExpired:
                loguru.logger.error(f"Masscan timed out for target: {target}")
                dead_hosts.append(target)
                continue
            if result.returncode != 0:
                loguru.logger.error(f"Masscan failed with error: {result.stderr}")
                dead_hosts.append(target)
                continue

            # 解析 masscan 的输出
            found_live_host = False
            for line in result.stdout.splitlines():
                if line and not line.startswith('#') and 'Discovered open port' in line:
                    parts = line.split()
                    if len(parts) >= 6:
                        ip = parts[5]  # IP 地址在第 6 个字段（索引为 5）
                        port_proto = parts[3]  # 端口号和协议在第 4 个字段（索引为 3）
                        port, proto = port_proto.split('/')  # 分割端口号和协议
                        port = int(port)  # 将端口号转换为整数
                        live_hosts.append({'ip': ip, 'port': port, 'proto': proto})
                        found_live_host = True

            if not found_live_host:
                dead_hosts.append(target)
        if dead_hosts and uuid:
            redis_key = f"scan:ports:dead_hosts:{uuid}"
            try:
                self.redis_cache.lpush(redis_key, *dead_hosts)
            except Exception as e:
                try:
                    self.redis_cache = RedisCache()
                    self.redis_cache.lpush(redis_key, *dead_hosts)
                except Exception as e:
                     loguru.logger.error(f"Failed to store dead hosts in Redis: {e}")

        return live_hosts

    def run_nmap_service_discovery(self, target: str, port: int) -> Dict[str, any]:
        """Run an Nmap service discovery scan on the specified target and port, output in XML format."""
        command = [
            self.nmap_bin, '-Pn', '-p', str(port), target,
            '--script', 'banner,http-title,http-server-header,http-enum',
            '-sV', '--version-all', '--osscan-guess', '-oX', '-'
        ]
        loguru.logger.debug(f"Executing: {' '.join(command)}")
        try:
            result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=60 * 5)
        except subprocess.TimeoutExpired:
            loguru.logger.error(f"Nmap timed out for target: {target}:{port}")
            return {}
        if result.returncode != 0:
            loguru.logger.error(f"Nmap failed with error: {result.stderr}")
            raise Exception(f"Nmap failed with error: {result.stderr}")

        # Parse the XML output using xmltodict
        parsed_output = self.parse_nmap_xml_output(result.stdout)
        # 确保 parsed_output 是一个字典
        if not isinstance(parsed_output, dict):
            loguru.logger.error(f"Parsed output is not a dictionary: {parsed_output}")
            return {}

        return parsed_output

    def parse_nmap_xml_output(self, xml_output: str) -> Dict[str, any]:
        """Parse the Nmap XML output string to extract service name, banner, and hostname."""
        import xmltodict
        data = xmltodict.parse(xml_output)
        print(f"Parsed Nmap XML output: {data}")
        if not data:
            return {}
        result = {}
        host = data.get('nmaprun', {}).get('host', {})
        # address = host.get('address', {}).get('@addr')
        # hostnames = host.get('hostnames', {}).get('hostname', [])
        # if isinstance(hostnames, dict):
        #     hostnames = [hostnames]
        # for hostname in hostnames:
        #     result['hostname'] = str(hostname.get('@name', ''))  # 确保转换为字符串
        ports = host.get('ports', {}).get('port', [])
        if isinstance(ports, dict):
            ports = [ports]
        for port in ports:
            port_id = port.get('@portid')
            protocol = port.get('@protocol')
            state = port.get('state', {}).get('@state')
            service = port.get('service', {})
            if service:
                service_name = service.get('@name')
                service_fp = service.get('@servicefp')
                product = service.get('@product')
                version = service.get('@version')
                extrainfo = service.get('@extrainfo')
                method = service.get('@method')
                conf = service.get('@conf')
                cpe = service.get('cpe')
                result['service_name'] = str(service_name)  # 确保转换为字符串
                result['service_fp'] = str(service_fp)  # 确保转换为字符串
                result['product'] = str(product)  # 确保转换为字符串
                result['version'] = str(version)  # 确保转换为字符串
                result['extrainfo'] = str(extrainfo)  # 确保转换为字符串
                result['method'] = str(method)  # 确保转换为字符串
                result['conf'] = str(conf)  # 确保转换为字符串
                result['cpe'] = str(cpe)  # 确保转换为字符串
            banner_script = port.get('script', {})
            if isinstance(banner_script, dict):
                banner_script = [banner_script]
            for script in banner_script:
                script_id = script.get('@id')
                script_output = script.get('@output')
                if script_id == 'banner':
                    result['banner'] = str(script_output)  # 确保转换为字符串
                elif script_id == 'http-title':
                    result['http_title'] = str(script_output)  # 确保转换为字符串
                elif script_id == 'http-server-header':
                    result['http_server_header'] = str(script_output)  # 确保转换为字符串
                elif script_id == 'http-enum':
                    result['http_enum'] = str(script_output)  # 确保转换为字符串
            result['port'] = str(port_id)  # 确保转换为字符串
            result['protocol'] = str(protocol)  # 确保转换为字符串
            result['state'] = str(state)  # 确保转换为字符串
            # result['ip'] = str(address)  # 确保转换为字符串
        return result

    def scan_and_discover(self, targets: str, top_ports: Union[str, List[int]] = 'top1000', masscan_rate: int = 1000,
                          uuid: str = None):
        """Conduct a comprehensive scan using masscan for quick host and port discovery followed by nmap service discovery."""
        parsed_targets = self.parse_targets(targets)
        print("Starting masscan for quick host and port discovery...")
        ports_to_scan = self.get_ports_to_scan(top_ports)
        live_hosts = None
        try:
            live_hosts = self.run_masscan(parsed_targets, ports=ports_to_scan, rate=masscan_rate, uuid=uuid)
        except Exception as e:
            print(f"Error running masscan: {e}")
        if not live_hosts:
            # self.redis_cache.update(f"scan:ports:result:{uuid}", uuid)
            return {}
        final_results = {}
        for host_info in live_hosts:
            ip = host_info['ip']
            port = host_info['port']
            try:
                nmap_output = self.run_nmap_service_discovery(ip, port)
            except Exception as e:
                print(f"Error running nmap on {ip}:{port}: {e}")
                nmap_output = None
            if nmap_output:
                final_results[(ip, port)] = nmap_output
                # 进行redis的存储
                try:
                    self.redis_cache.update(f"scan:ports:single:{uuid}:{ip}:{port}", str(nmap_output), -1)
                except Exception as e:
                    try:
                        self.redis_cache = RedisCache()
                        self.redis_cache.update(f"scan:ports:single:{uuid}:{ip}:{port}", str(nmap_output), -1)
                    except Exception as e:
                        loguru.logger.error(f"Failed to store dead hosts in Redis: {e}")
        self.redis_cache.update(f"scan:ports:result:{uuid}", uuid)
        return final_results


if __name__ == "__main__":
    scanner = PortScanner()
    try:
        # You can specify multiple targets separated by commas, CIDR notation, or IP ranges with hyphens
        # And you can specify 'top100', 'top1000', 'all', or a custom list of ports
        results = scanner.scan_and_discover(
            targets='127.0.0.1',
            # top_ports=['22', '28888', '3306']  # Custom list of ports
            top_ports='all',
            masscan_rate=1000,
            uuid='1234567890'
        )
        print(f"Scan completed. Found {results} open ports.")
        print(json.dumps(results, indent=4))
        # results = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE nmaprun><?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?><!-- Nmap 7.80 scan initiated Sun Jan 12 10:52:35 2025 as: nmap -Pn -sV -O -p 28888 -oX - 1.92.64.83 --><nmaprun scanner="nmap" args="nmap -Pn -sV -O -p 28888 -oX - 1.92.64.83" start="1736650355" startstr="Sun Jan 12 10:52:35 2025" version="7.80" xmloutputversion="1.04"><scaninfo type="syn" protocol="tcp" numservices="1" services="28888"/><verbose level="0"/><debugging level="0"/><host starttime="1736650355" endtime="1736650432"><status state="up" reason="user-set" reason_ttl="0"/><address addr="1.92.64.83" addrtype="ipv4"/><hostnames><hostname name="ecs-1-92-64-83.compute.hwclouds-dns.com" type="PTR"/></hostnames><ports><port protocol="tcp" portid="28888"><state state="open" reason="syn-ack" reason_ttl="58"/><service name="unknown" servicefp="SF-Port28888-TCP:V=7.80%I=7%D=1/12%Time=67832E79%P=x86_64-pc-linux-gnu%r(GenericLines,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(GetRequest,13F,&quot;HTTP/1\.1\x20200\x20OK\r\nConnection:\x20close\r\nVary:\x20Origin\r\nVary:\x20Access-Control-Request-Method\r\nVary:\x20Access-Control-Request-Headers\r\nContent-Type:\x20text/plain;charset=UTF-8\r\nContent-Length:\x2095\r\nDate:\x20Sun,\x2012\x20Jan\x202025\x2002:52:41\x20GMT\r\n\r\n\xe6\xac\xa2\xe8\xbf\x8e\xe4\xbd\xbf\xe7\x94\xa8csap-web\xe5\x90\x8e\xe5\x8f\xb0\xe7\xae\xa1\xe7\x90\x86\xe6\xa1\x86\xe6\x9e\xb6\xef\xbc\x8c\xe5\xbd\x93\xe5\x89\x8d\xe7\x89\x88\xe6\x9c\xac\xef\xbc\x9av1\.0\.0\xef\xbc\x8c\xe8\xaf\xb7\xe9\x80\x9a\xe8\xbf\x87\xe5\x89\x8d\xe7\xab\xaf\xe5\x9c\xb0\xe5\x9d\x80\xe8\xae\xbf\xe9\x97\xae\xe3\x80\x82&quot;)%r(HTTPOptions,10C,&quot;HTTP/1\.1\x20200\x20OK\r\nConnection:\x20close\r\nVary:\x20Origin\r\nVary:\x20Access-Control-Request-Method\r\nVary:\x20Access-Control-Request-Headers\r\nContent-Type:\x20application/json\r\nDate:\x20Sun,\x2012\x20Jan\x202025\x2002:52:41\x20GMT\r\n\r\n{\&quot;code\&quot;:401,\&quot;msg\&quot;:\&quot;\xe8\xae\xa4\xe8\xaf\x81\xe5\xa4\xb1\xe8\xb4\xa5\xef\xbc\x8c\xe6\x97\xa0\xe6\xb3\x95\xe8\xae\xbf\xe9\x97\xae\xe7\xb3\xbb\xe7\xbb\x9f\xe8\xb5\x84\xe6\xba\x90\&quot;,\&quot;data\&quot;:null}&quot;)%r(RTSPRequest,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(Help,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(SSLSessionReq,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(TerminalServerCookie,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(TLSSessionReq,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(Kerberos,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(SMBProgNeg,42,&quot;HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Length:\x200\r\nConnection:\x20close\r\n\r\n&quot;)%r(FourOhFourRequest,A6,&quot;HTTP/1\.1\x20200\x20OK\r\nConnection:\x20close\r\nContent-Type:\x20text/plain;charset=utf-8\r\nDate:\x20Sun,\x2012\x20Jan\x202025\x2002:53:01\x20GMT\r\n\r\n\xe9\x9d\x9e\xe6\xb3\x95\xe8\xaf\xb7\xe6\xb1\x82\xef\xbc\x9a/nice%20ports%2C/Tri%6Eity\.txt%2ebak&quot;);" method="table" conf="3"/></port></ports><os><portused state="open" proto="tcp" portid="28888"/><osmatch name="Linux 2.6.32" accuracy="93" line="55173"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:2.6.32</cpe></osclass></osmatch><osmatch name="Linux 3.10" accuracy="93" line="62820"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:3.10</cpe></osclass></osmatch><osmatch name="Linux 3.10 - 4.11" accuracy="93" line="63230"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:3</cpe></osclass><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="4.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:4</cpe></osclass></osmatch><osmatch name="Linux 3.2 - 4.9" accuracy="93" line="65105"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:3</cpe></osclass><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="4.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:4</cpe></osclass></osmatch><osmatch name="Linux 3.4 - 3.10" accuracy="93" line="65366"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="93"><cpe>cpe:/o:linux:linux_kernel:3</cpe></osclass></osmatch><osmatch name="Linux 2.6.32 - 3.10" accuracy="92" line="56381"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="92"><cpe>cpe:/o:linux:linux_kernel:2.6</cpe></osclass><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="92"><cpe>cpe:/o:linux:linux_kernel:3</cpe></osclass></osmatch><osmatch name="Linux 2.6.32 - 3.13" accuracy="92" line="56411"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="92"><cpe>cpe:/o:linux:linux_kernel:2.6</cpe></osclass><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="3.X" accuracy="92"><cpe>cpe:/o:linux:linux_kernel:3</cpe></osclass></osmatch><osmatch name="Synology DiskStation Manager 5.2-5644" accuracy="91" line="101159"><osclass type="storage-misc" vendor="Synology" osfamily="DiskStation Manager" osgen="5.X" accuracy="91"><cpe>cpe:/a:synology:diskstation_manager:5.2</cpe></osclass></osmatch><osmatch name="Linux 2.6.22 - 2.6.36" accuracy="89" line="51668"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="89"><cpe>cpe:/o:linux:linux_kernel:2.6</cpe></osclass></osmatch><osmatch name="Linux 2.6.39" accuracy="89" line="58000"><osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="89"><cpe>cpe:/o:linux:linux_kernel:2.6.39</cpe></osclass></osmatch></os><uptime seconds="3397573" lastboot="Wed Dec  4 03:07:39 2024"/><tcpsequence index="262" difficulty="Good luck!" values="6FF6C27,1AE6CDE9,7FFE755B,DBCAF28B,A66A8A8D,28B84FF8"/><ipidsequence class="All zeros" values="0,0,0,0,0,0"/><tcptssequence class="1000HZ" values="CA82D164,CA82D1C8,CA82D22C,CA82D291,CA82D2F4,CA82D359"/><times srtt="1691" rttvar="418" to="100000"/></host><runstats><finished time="1736650432" timestr="Sun Jan 12 10:53:52 2025" elapsed="76.69" summary="Nmap done at Sun Jan 12 10:53:52 2025; 1 IP address (1 host up) scanned in 76.69 seconds" exit="success"/><hosts up="1" down="0" total="1"/></runstats></nmaprun>'
        # scanner.parse_nmap_xml_output(results)
    except Exception as e:
        print(e)
